package main

import (
	"fmt"
	"time"

	"github.com/go-redis/redis"
)

// 声明一个全局的rdb变量
var rdb *redis.Client

// 初始化连接
func initClient() (err error) {
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // 密码
		DB:       0,  // 数据库
		PoolSize: 20, // 连接池大小
	}) //传入连接的选项

	_, err = rdb.Ping().Result()
	return err
}

func redisExample() {
	err := rdb.Set("score", 100, 0).Err()
	if err != nil {
		fmt.Printf("set score failed, err:%v\n", err)
		return
	}

	val, err := rdb.Get("score").Result()
	if err != nil {
		fmt.Printf("get score failed, err:%v\n", err)
		return
	}
	fmt.Println("score", val)

	val2, err := rdb.Get("name").Result() //Result返回值和错误 val只返回值
	if err == redis.Nil {
		fmt.Println("name does not exist")
	} else if err != nil {
		fmt.Printf("get name failed, err:%v\n", err)
		return
	} else {
		fmt.Println("name", val2)
	}
}

// 哈希相关
func hgetDemo() {
	v, err := rdb.HGetAll("user").Result() //HGetAll 获取哈希里所有的字段
	if err != nil {
		//redis.Nil
		//其他错误
		fmt.Printf("hgetall failed, err:%v\n", err)
		return
	}
	fmt.Println(v)

	v2 := rdb.HMGet("user", "name", "age").Val() //根据指定字段查询
	fmt.Println(v2)

	v3 := rdb.HGet("user", "age").Val() //只取出"age"字段
	fmt.Println(v3)
}

// zsetDemo 操作zset示例 有序的集合
func zsetDemo() {
	// key
	zsetKey := "language_rank"
	// value
	// 注意：v8版本使用[]*redis.Z；此处为v9版本使用[]redis.Z
	languages := []redis.Z{
		{Score: 90.0, Member: "Golang"},
		{Score: 98.0, Member: "Java"},
		{Score: 95.0, Member: "Python"},
		{Score: 97.0, Member: "JavaScript"},
		{Score: 99.0, Member: "C/C++"},
	}
	//ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	//defer cancel()

	// ZADD
	err := rdb.ZAdd(zsetKey, languages...).Err()
	if err != nil {
		fmt.Printf("zadd failed, err:%v\n", err)
		return
	}
	fmt.Println("zadd success")

	// 把Golang的分数加10
	newScore, err := rdb.ZIncrBy(zsetKey, 10.0, "Golang").Result()
	if err != nil {
		fmt.Printf("zincrby failed, err:%v\n", err)
		return
	}
	fmt.Printf("Golang's score is %f now.\n", newScore)

	// 取分数最高的3个
	ret := rdb.ZRevRangeWithScores(zsetKey, 0, 2).Val()
	for _, z := range ret {
		fmt.Println(z.Member, z.Score)
	}

	// 取95~100分的
	op := redis.ZRangeBy{
		Min: "95",
		Max: "100",
	}
	ret, err = rdb.ZRangeByScoreWithScores(zsetKey, op).Result()
	if err != nil {
		fmt.Printf("zrangebyscore failed, err:%v\n", err)
		return
	}
	for _, z := range ret {
		fmt.Println(z.Member, z.Score)
	}
}

// watchDemo 在key值不变的情况下将其值+1
func watchDemo(key string) error {
	//监视 key 的值, 并在值不变的前提下将其值+1
	return rdb.Watch(func(tx *redis.Tx) error {
		n, err := tx.Get(key).Int()
		if err != nil && err != redis.Nil {
			return err
		}
		// 假设操作耗时5秒
		// 5秒内我们通过其他的客户端修改key，当前事务就会失败
		time.Sleep(5 * time.Second)
		_, err = tx.TxPipelined(func(pipe redis.Pipeliner) error {
			//业务逻辑
			pipe.Set(key, n+1, time.Hour)
			return nil
		})
		return err
	}, key)
}

func main() {
	err := initClient()
	if err != nil {
		fmt.Printf("init Client err:%v\n", err)
	}
	fmt.Println("connect redis success.")
	// 程序退出时释放redis资源
	defer rdb.Close()
	//redisExample()
	//hgetDemo()
	//zsetDemo()
	err = watchDemo("watch_count")
	if err != nil {
		fmt.Printf("watchDemo failed, err :=%v\n", err)
	}
}

// hset user name "flm"
// hgetall user
